#include "/project/public/_public.h"

#define MAXNUMP_ 1000		//最大的进程数量
#define SHMKEYP_ 0x5666		//共享内存的key
#define SEMKEYP_ 0x8888 	//信号量的key

//进程心跳信息的结构体
struct st_pinfo
{
	int pid;				//进程的id
	char pname[64];			//进程的名称,可以为空
	int timeout;			//超时时间,单位为秒
	time_t atime;			//最后一次心跳的时间,用长整形表示
};

int main(int argc,char* argv[])
{
	if(argc < 2)
	{
		std::cout<<"Using:./book procname"<<std::endl;
		return -1;
	}

	//创建/获取共享内存,大小为n*sizeof(struct st_prinfo)
	int shmid_ = 0;
	if((shmid_ = shmget(SHMKEYP_,MAXNUMP_*sizeof(st_pinfo),0640|IPC_CREAT)) ==-1)
	{
		std::cout<<"shmget() failed\n"<<std::endl;
		return -1;
	}
	//创建当前进程的心跳信息结构体变量,把本进程的信息填进去
	st_pinfo stpinfo;
	memset(&stpinfo,0,sizeof(st_pinfo));
	stpinfo.pid = getpid();
	STRCPY(stpinfo.pname,sizeof(stpinfo.pname),argv[1]);
	//std::cout<<"stpinfo.pname"<<stpinfo.pname<<std::endl;
	stpinfo.timeout = 30;
	stpinfo.atime = time(NULL);
	
	//给共享内存加锁
	CSEM sem_;
	if(sem_.init(SEMKEYP_) == false)
	{
		std::cout<<"sem_.init("<<SEMKEYP_<<") failed"<<std::endl;
		return -1;
	}
	//将共享内存连接到当前进程的地址空间
	struct st_pinfo* shm_;
	shm_ =(st_pinfo*)shmat(shmid_,0,0);
	
	//在极少的情况下有可能上一个进程意外总结没有删除心跳信息,而本进程生成的心跳信息pid又与上一个进程pid相同,轮询时会导致守护误认当前进程已经退出而删除心跳信息的可能,因此需要先查询是否存在当前进程相同的pid的进程信息
	int pos = -1;
	for(int i = 0; i < MAXNUMP_; ++i)
	{
		if((shm_+i)->pid == stpinfo.pid)
		{
			pos = i;
			memcpy(shm_+i,&stpinfo,sizeof(stpinfo));
		}
	}
	//在共享内存中查找一个空位置,把当前进程的心跳信息存入共享内存中
	if(-1 == pos)
	{
		sem_.P();
		//std::cout<<"shm_->pid = "<<shm_->pid<<std::endl;
		for(int i = 0; i < MAXNUMP_; ++i)
		{
			if((shm_+i)->pid == 0)
			{
				pos = i;
				break;
			}
		}

		//std::cout<<"pos = "<<pos<<std::endl;

		if(pos == -1)
		{
			sem_.V();
			std::cout<<"共享内存空间已经用完."<<std::endl;
			return -1;
		}

		memcpy(shm_+pos,&stpinfo,sizeof(stpinfo));
		sem_.V();
	}
	while(true)
	{
		//更新共享内存中本进程的心跳时间
		shm_[pos].atime = time(NULL);
		sleep(10);
	}

	//把当前进程的信息从共享内存中移去
	//shm_[pos].pid = 0;
	memset(shm_+pos,0,sizeof(struct st_pinfo));

	//把共享内存从当前进程中分离
	shmdt(shm_);
	return 0;
}
